#include <modbus.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdlib.h>
modbus_t *ctx;
pthread_mutex_t mutex;
char s[8];
struct msgbuf // 消息队列
{
    long mtype; // 第一个成员必须是long类型变量，表示消息的类型
    int cmd1;
    int cmd2;
};
struct msgbuf msg;
key_t key;

void *readholding(void *arg)    //读取保持寄存器
{
    uint16_t buf[256] = {0};
    char arr[256] = {0};
    // 创建共享内存
    int shmid;
    shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 0777);
    if (shmid < 0)
    {
        // 打开
        if (errno == EEXIST)
            shmid = shmget(key, 64, 0777);
        else
        {
            perror("shmget err");
            return NULL;
        }
    }
    printf("%d\n", shmid);
    // 映射
    char *p = NULL;
    p = (char *)shmat(shmid, NULL, 0);  // 映射共享内存到进程地址空间
    if (p == (char *)-1)                  // 映射失败
    {
        perror("shmat err");
        return NULL;
    }
    while (1)
    {
        pthread_mutex_lock(&mutex);

        int nb = modbus_read_registers(ctx, 0, 3, buf); // 读取保持寄存器
        if (nb == -1)
        {
            perror("modbus_read_registers");
            break; // 读取失败，退出循环
        }
        sprintf(arr, "%d,%d,%d", buf[0], buf[1], buf[2]);
        strcpy(p, arr); // 将读取到的数据复制到共享内存中
        printf("%s\n", p);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }

    pthread_exit(NULL);
}

void *writeled(void *arg)
{
    int cmd1, cmd2;
    // 创建消息队列
    int msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
    if (msgid < 0)
    {
        if (errno == EEXIST)
            msgid = msgget(key, 0666);
        else
        {
            perror("msgget err");
            return NULL;
        }
    }

    // 计算消息正文的大小
    size_t s = sizeof(struct msgbuf) - sizeof(long);
    while (1)
    {
        // 接收控制指令
        msgrcv(msgid, &msg, s, 1, 0);
        printf("cmd1=%d cmd2=%d\n", msg.cmd1, msg.cmd2);
        if (msg.cmd1 == 0 && msg.cmd2 == 0)
            printf("LED灯关\n");
        else if (msg.cmd1 == 0 && msg.cmd2 == 1)
            printf("LED灯开\n");
        else if (msg.cmd1 == 1 && msg.cmd2 == 0)
            printf("蜂鸣器关\n");
        else if (msg.cmd1 == 1 && msg.cmd2 == 1)
            printf("蜂鸣器开\n");
        modbus_write_bit(ctx, msg.cmd1, msg.cmd2); // 写单个线圈
    }
    msgctl(msgid, IPC_RMID, NULL); // 删除消息队列
    return NULL;
}

int main(int argc, char const *argv[])
{
    ctx = modbus_new_tcp("192.168.1.13", 502); // Windows IP
    if (ctx == NULL)
    {
        perror("modbus_new_tcp");
        return -1;
    }
    // 创建key值
    key = ftok("./app", 'b');
    if (key < 0)
    {
        perror("ftok err");
    }
    printf("%#x\n", key);

    int slaveid = modbus_set_slave(ctx, 01); // 设置从机id
    if (slaveid == -1)
    {
        perror("modbus_set_slave");
        return -1;
    }

    if (modbus_connect(ctx) == -1) // 和从机进行连接
    {
        perror("modbus_connect");
        return -1;
    }

    pthread_t tid;
    if (pthread_mutex_init(&mutex, NULL) != 0)
    {
        perror("pthread_mutex_init");
        modbus_close(ctx);
        modbus_free(ctx);
        return -1;
    }

    if (pthread_create(&tid, NULL, readholding, &ctx) != 0)
    {
        perror("pthread_create readholding");
        pthread_mutex_destroy(&mutex);
        modbus_close(ctx);
        modbus_free(ctx);
        return -1;
    }

    if (pthread_create(&tid, NULL, writeled, NULL) != 0)
    {
        perror("pthread_create");
        pthread_mutex_destroy(&mutex);
        modbus_close(ctx);
        modbus_free(ctx);
        return -1;
    }

    pthread_join(tid, NULL); // 线程分离
    pthread_mutex_destroy(&mutex);

    modbus_close(ctx); // 关闭套接字
    modbus_free(ctx);  // 释放实例
    return 0;
}
